Skip to content

Add QUIC support#1008

Open
tenderlove wants to merge 5 commits intoruby:masterfrom
tenderlove:quic-api
Open

Add QUIC support#1008
tenderlove wants to merge 5 commits intoruby:masterfrom
tenderlove:quic-api

Conversation

@tenderlove
Copy link
Member

@tenderlove tenderlove commented Feb 23, 2026

This commit exposes various QUIC methods so that you can build QUIC clients and servers.

I'm really sorry about the very large patch. I can split it in to smaller patches if that helps to get this upstream. I think the patch is pretty straightforward though. It just adds different QUIC related methods that OpenSSL provides. I've been using this patch to develop a QUIC/HTTP3 client and server in Ruby.

My development branch is here. Here is an example script that uses these APIs.

@tenderlove
Copy link
Member Author

tenderlove commented Feb 25, 2026

I've been able to serve a Rails application with QUIC+HTTP/3 using this patch:

Screenshot 2026-02-25 at 8 55 55 AM

@rhenium
Copy link
Member

rhenium commented Feb 26, 2026

Thanks for working on this!

I wonder if QUIC listener SSL, QUIC connection SSL, and QUIC stream SSL should be represented as separate (sub)classes. Although they all have the same type SSL, they support different sets of methods, so that would make

I think the blocking methods such as #read and #wait_readable need to be updated to check SSL_poll() rather than just the readiness of the underlying socket. I imagine currently SSLSocket#read can enter a busy loop if the stream is blocked by another stream on the same connection, even when the underlying socket is readable.

Regarding the thread assisted mode (OSSL_QUIC_client_thread_method() and SSL_set_blocking_mode()), please note that we currently don't release the GVL when calling SSL_read(). It may take some work to do so, as it needs fixing callbacks (e.g., SSLContext#new_session_cb which can be called at any time in the connection lifecycle) to re-acquire it as necessary. I'm not so sure if supporting this mode is worthwhile at the moment, especially since the server side support is still missing in OpenSSL and we have to consider both. It appears you didn't use this mode in the kantan repo either.

@ioquatix, what do you think?

have_func("SSL_get_accept_connection_queue_len(NULL)", ssl_h)
have_func("SSL_listen(NULL)", ssl_h)
have_func("SSL_poll(NULL, 0, 0, NULL, 0, NULL)", ssl_h)
have_func("SSL_set_incoming_stream_policy(NULL, 0, 0)", ssl_h)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can simply require OpenSSL 3.5+ and remove most of the conditionals. There is little need to support 3.2-3.4 which are non-LTS releases and will reach EOL in 8 months, and we want the server side support in 3.5 to write meaningful tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, but I'm not sure how to do that. How do we take other SSL implementations in to account?


GetSSL(self, ssl);
return SSL_is_init_finished(ssl) ? Qtrue : Qfalse;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to QUIC support? Is this different from seeing whether #accept/#connect/#accept_connection successfully returned or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I'm using it on the server side to detect if the handshake has finished here. Without this, open_streams can raise an exception.

Comment on lines +3134 to +3141
/*
* call-seq:
* ssl.accept_connection(flags = 0) => SSLSocket or nil
*
* Accepts an incoming QUIC connection from the listener. Returns a new
* SSLSocket representing the connection, or +nil+ if no connection is
* available (when using non-blocking mode or ACCEPT_CONNECTION_NO_BLOCK).
*/
Copy link
Member

@rhenium rhenium Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to provide a blocking and non-blocking variants, similar to TCPServer#accept and #accept_nonblock?

Maybe also for #accept_stream.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that makes sense. From reading OpenSSL source code, NO_BLOCK is the only flag it cares about.

I'll change this to accept and add an accept_nonblock

This commit exposes various QUIC methods so that you can build QUIC
clients and servers
@tenderlove
Copy link
Member Author

I wonder if QUIC listener SSL, QUIC connection SSL, and QUIC stream SSL should be represented as separate (sub)classes. Although they all have the same type SSL, they support different sets of methods, so that would make

I'm not sure. We could do that, and I'm happy to do that work. It's up to you.

Regarding the thread assisted mode (OSSL_QUIC_client_thread_method() and SSL_set_blocking_mode()), please note that we currently don't release the GVL when calling SSL_read(). It may take some work to do so, as it needs fixing callbacks (e.g., SSLContext#new_session_cb which can be called at any time in the connection lifecycle) to re-acquire it as necessary. I'm not so sure if supporting this mode is worthwhile at the moment, especially since the server side support is still missing in OpenSSL and we have to consider both. It appears you didn't use this mode in the kantan repo either.

I think we shouldn't support it, and I'll remove it. I've updated the patch to make QUIC connections always non-blocking, and I think that's the way we should go. I've removed the accessor to SSL_set_blocking_mode, and I'll remove the OSSL_QUIC_client_thread_method. I tried using OSSL_QUIC_client_thread_method, but ran in to exactly the GVL problem, so changed to non-blocking mode in my code.

@tenderlove
Copy link
Member Author

I wonder if QUIC listener SSL, QUIC connection SSL, and QUIC stream SSL should be represented as separate (sub)classes. Although they all have the same type SSL, they support different sets of methods, so that would make

I'm not sure. We could do that, and I'm happy to do that work. It's up to you

Ah, I remember. I had considered adding a subclass as you say, but I've always run in to issues when trying to work with a subclass on T_DATA objects. I think we should not make it a subclass, but my only reason is from having a bad experience in the past.

Clients should use IO.select and non-blocking sockets instead
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants